using System;
using System.Collections.Generic;
using System.Xml.Linq;
using CthulhuDomain.GameEntities;
using CthulhuDomain.StageManagement;
using CthulhuDomain.Strategies;
using Microsoft.Xna.Framework.GamerServices;
using System.Linq;

namespace CthulhuDomain.Services
{
  public class CollisionManager
  {
    #region

    private static volatile CollisionManager instance;
    private static readonly object syncRoot = new Object();

    public static CollisionManager Instance {
      get {
        if (instance == null) {
          lock (syncRoot) {
            if (instance == null)
              instance = new CollisionManager();
          }
        }

        return instance;
      }
    }

    protected static IGameServiceProvider GameServiceManager { get; set; }

    internal static LevelStage CurrentStage { get; set; }
    public static void Initialize(IGameServiceProvider gameServiceProvider, LevelStage levelStage) {
      GameServiceManager = gameServiceProvider;
      CurrentStage = levelStage;
    }

    #endregion

    internal static ScoreManager.CollisionResult DetectCollision(DomainEntityMatrix matrix, DomainEntity player) {
      var result = new ScoreManager.CollisionResult() { Amount = 0, ResultType = ScoreManager.CollisionResult.eResultType.None };

      for (var i = 0; i < matrix.XLength; i++)
        for (var j = 0; j < matrix.YLength; j++) {
          if (matrix.GetElement(i, j) == null || player == null) continue;
          if (matrix.GetElement(i, j).Intersect(player)) {
            player = Strike(player, matrix.GetElement(i, j));
            if (Convert.ToBoolean(player == null)) {
              result.ResultType = ScoreManager.CollisionResult.eResultType.PlayerDie;
            }
            break;
          }
          result = CheckPlayerCollision(ref player, matrix, i, j, result);
          if (!Convert.ToBoolean(result.ResultType & ScoreManager.CollisionResult.eResultType.PlayerDie))
            CheckArmyCollision(player, matrix, i, j, result);
        }

      if (matrix.EntityCount() == 0)
        result.ResultType |= ScoreManager.CollisionResult.eResultType.StageComplete;

      return result;
    }

    public static void AddPowerUp(DomainEntity powerUp) {
      CurrentStage.PowerUps.Add((PowerUp)powerUp);
    }

    public static void AddShoot(DomainEntity powerUp) {
      CurrentStage.Shoots.Add((Shoot)powerUp);
    }

    public static Shoot GetShoot(int index) {
      return CurrentStage.Shoots.Get(index);
    }

    public static PowerUp GetPowerUp(int index)
    {
      return CurrentStage.PowerUps.Get(index);
    }

    static ScoreManager.CollisionResult CheckPlayerCollision(ref DomainEntity player, DomainEntityMatrix matrix, int x, int y, ScoreManager.CollisionResult result) {
      var entity = player;
      CheckPowerUps(result, entity);
      
      CheckShoots(result, entity);

      return result;
    }

    private static void CheckShoots(ScoreManager.CollisionResult result, DomainEntity entity)
    {
      var count = CurrentStage.Shoots.Count;
      var toRemove = new List<Shoot>();
      for (var i = 0; i < count; i++) {
        if (!CurrentStage.Shoots.Get(i).Intersect(entity)) continue;
        var entityDamage = entity.Damage;
        entity = Strike(entity, CurrentStage.Shoots.Get(i));
        if (entity == null)
          result.ResultType |= ScoreManager.CollisionResult.eResultType.PlayerDie;

        toRemove.Add(CurrentStage.Shoots.Get(i));
        break;

      }

      foreach (var shoot in toRemove)
        CurrentStage.Shoots.Remove(shoot);
    }

    private static void CheckPowerUps(ScoreManager.CollisionResult result, DomainEntity entity)
    {
      var count = CurrentStage.PowerUps.Count;
      var toRemove = new List<PowerUp>();
      for (var i = 0; i < count; i++)
      {
        if (!CurrentStage.PowerUps.Get(i).Intersect(entity)) continue;
        result.Modifier = ((IPower)CurrentStage.PowerUps.Get(i)).Power;
        result.ResultText = ((IPower)CurrentStage.PowerUps.Get(i)).Power.TextMessage;
        toRemove.Add(CurrentStage.PowerUps.Get(i));
      }

      foreach (var powerUp in toRemove)
        CurrentStage.PowerUps.Remove(powerUp);
    }

    static void CheckArmyCollision(DomainEntity player, DomainEntityMatrix matrix, int x, int y, ScoreManager.CollisionResult result) {
      for (var k = 0; k < player.Shoots.Count; k++) {
        if (player.GetShoot(k) == null || !player.GetShoot(k).Intersect(matrix.GetElement(x, y))) continue;
        var amount = matrix.GetAmount(x, y);
        var matrixDamage = matrix.GetDamage(x, y);
        var bangPosition = new Vector2Adapter(matrix.GetElement(x, y).Position.X, matrix.GetElement(x, y).Position.Y + matrix.GetElement(x, y).Height / 2);
        matrix.SetElement(x, y, Strike(matrix.GetElement(x, y), player.GetShoot(k)));
        if (matrix.GetElement(x, y) == null) {
          result.IncreaseScore(amount);
          var factory = DomainFactory.GetFactory(DomainFactory.eTypeEntity.Bang);
          DomainEntity entity;
          try {
            entity = factory.CreateEntity(GameServiceManager.ContentManager, new BangState { LifePoints = 0, StartingPosition = bangPosition });
          }
          catch (Exception) {
            throw new GamerServicesNotAvailableException();
          }
          matrix.SetElement(x, y, entity);
        }

        player.SetShoot(k, Strike(player.GetShoot(k), matrixDamage));
      }
    }

    static DomainEntity Strike(DomainEntity target, DomainEntity damager) {
      return Strike(target, damager.Damage);
    }

    static DomainEntity Strike(DomainEntity target, int damage) {
      if (target.LifePoint > 0)
        target.LifePoint -= damage;

      if (Equals(target.LifePoint, 0))
        target = null;

      return target;
    }
  }
}